
////////////////////////////////////////////////////////////////////////////
//	File Name: glibplus.cpp		Last modification at : 10/25/2002 11:23:44 AM
//	Copyright 1999-2002 Microsoft Corporation. All Rights Reserved.
//
//	Author:      Rong Xiao
//	Email :      i-rxiao@microsoft.com
//
//	Description : 
//
//	Revision History: 
//
////////////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "glibplus.h"
#include "ImageWrapper.h"
#include <iostream>
using namespace std;
using namespace Gdiplus;

int GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
   UINT  num = 0;          // number of image encoders
   UINT  size = 0;         // size of the image encoder array in bytes

   ImageCodecInfo* pImageCodecInfo = NULL;

   GetImageEncodersSize(&num, &size);
   if(size == 0)
      return -1;  // Failure

   pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
   if(pImageCodecInfo == NULL)
      return -1;  // Failure

   GetImageEncoders(num, size, pImageCodecInfo);

   for(UINT j = 0; j < num; ++j)
   {
      if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
      {
         *pClsid = pImageCodecInfo[j].Clsid;
         free(pImageCodecInfo);
         return j;  // Success
      }    
   }

   free(pImageCodecInfo);
   return -1;  // Failure
}

void CGdiImage::Free()
{
	if ( m_pImage && (m_iLocked==0) ) {
		delete m_pImage;	
		m_pImage=0;
	}
}
bool CGdiImage::ConvertTo24BitBmp()
{
	if (!m_pImage) return false;
	TUIntMat mat;
	Gdiplus::PixelFormat pixelFormat = m_pImage->GetPixelFormat();
	if (pixelFormat == PixelFormat24bppRGB) 
		return true;
	else if (pixelFormat == PixelFormat8bppIndexed ) {
		Img2RGBMat(mat);
		RGBMat2Img(mat);
		return true;
	}
	else 
		return false;  //unsupported format
}

bool CGdiImage::ConvertTo8BitBmp()
{
	if (!m_pImage) return false;
	TByteMat mat;
	Gdiplus::PixelFormat pixelFormat = m_pImage->GetPixelFormat();
	if (pixelFormat == PixelFormat8bppIndexed) 
		return true;
	else if (pixelFormat == PixelFormat24bppRGB || pixelFormat == PixelFormat32bppRGB) {
		Img2Mat(mat);
		Mat2Img(mat);
		return true;
	}
	else 
		return false;  //unsupported format
}

bool CGdiImage::Load(const char* pszFileName)
{
	WCHAR wzFileName[_MAX_PATH];


	Free();

	swprintf_s( wzFileName, _MAX_PATH, L"%S", pszFileName );
	m_pImage=Bitmap::FromFile( wzFileName );

	if ( m_pImage == NULL || m_pImage->GetLastStatus() != Ok ) {
		cout << "Error: Failed to open file " << pszFileName << endl;
		if ( m_pImage )
			delete m_pImage;
		m_pImage=0;
		return false;
	}
 
	
	Gdiplus::PixelFormat pixelFormat = m_pImage->GetPixelFormat();
	
	m_ByteCount=GetPixelByte(pixelFormat);
 
	return true;
}

void CGdiImage::Rotate(float flRotate)
{
	int nWidth = m_pImage->GetWidth();
	int nHeight = m_pImage->GetHeight();

	Rect rc(0, 0, nWidth, nHeight);
	
	//image.ConvertTo24BitBmp();
	Bitmap imageTmp(nWidth,nHeight, PixelFormat24bppRGB );
	Graphics gr(&imageTmp),grSrc(m_pImage);
	Matrix matrix;
	matrix.RotateAt(flRotate, 
		PointF(nWidth/2.0f, nHeight/2.0f),
		MatrixOrderPrepend);

	gr.SetTransform(&matrix);

	gr.DrawImage(m_pImage,
		rc, 
		0, 0, nWidth, nHeight,
		UnitPixel,	NULL,	NULL, NULL);

	grSrc.DrawImage(&imageTmp,
		rc, 
		0, 0, nWidth, nHeight,
		UnitPixel,	NULL,	NULL, NULL);
}

void  CGdiImage::Crop(RECT& rect)
{
	int nWidth = rect.right - rect.left;
	int nHeight = rect.bottom - rect.top;

	Rect rc(0, 0, nWidth, nHeight);

	//image.ConvertTo24BitBmp();
	
	Bitmap* pImg =  new  Gdiplus::Bitmap(nWidth,nHeight,PixelFormat24bppRGB);

	Graphics gr(pImg);
	gr.DrawImage(m_pImage,
		rc, 
		rect.left, rect.top, nWidth, nHeight,
		UnitPixel,	NULL,	NULL, NULL);
	Free();
	m_pImage = pImg;
}
//
//void CGdiImage::Rotate(CGdiImage& img, PointF& center, float flRotate, BOOL bExpand)
//{
//
//	Free();
//
//	int nWidth = img.GetWidth();
//	int nHeight = img.GetHeight();
//
//	int nDstWidth=nWidth, nDstHeight=nHeight;
//
//	TGlibPoint<float> rectPoints[] = {
//		TGlibPoint<float>(0,0),
//		TGlibPoint<float>(nWidth,0),
//		TGlibPoint<float>(0,nHeight),
//		TGlibPoint<float>(nWidth,nHeight)
//	};
//	TGlibPoint<float> centP(center.X, center.Y);
//
//	rectPoints[0].Rotate(centP, flRotate/180*3.14159265359f);
//
//	float fMinX, fMinY,fMaxX, fMaxY;
//	
//	fMinX = fMaxX = rectPoints[0].m_X;
//	fMinY = fMaxY = rectPoints[0].m_Y;
//	for ( int i=1; i<4; i++)
//	{
//		rectPoints[i].Rotate(centP,flRotate/180*3.14159265359f);
//		TUpdateMinMax(fMinX,fMaxX, rectPoints[i].m_X);
//		TUpdateMinMax(fMinY,fMaxY, rectPoints[i].m_Y);
//	}
//
//	Matrix matrix;
//	matrix.RotateAt(flRotate, 	PointF(center)	);
//
//	if (bExpand==TRUE)
//	{
//		matrix.Translate(-fMinX,-fMinY,MatrixOrderAppend);
//		nDstWidth = int(fMaxX -fMinX+0.5f);
//		nDstHeight = int(fMaxY -fMinY+0.5f);
//	}
//
//	m_pImage= new  Gdiplus::Bitmap(nDstWidth,nDstHeight,PixelFormat24bppRGB);
//	Bitmap* m_pImgSrc = img.LockBmp();
//	Graphics gr(m_pImage);
//
//	gr.SetTransform(&matrix);
//
//	gr.DrawImage(m_pImgSrc,
//		Rect(0,0,nWidth, nHeight), 
//		0, 0, nWidth, nHeight,
//		UnitPixel,	NULL,	NULL, NULL);
//	img.UnLockBmp();
//}

bool Img2Mat(Gdiplus::Bitmap *pImage, TByteMat& mat)
{
	if ( !pImage) 
		return false;

	Rect rc(0, 0, pImage->GetWidth(), pImage->GetHeight());
	Gdiplus::BitmapData bmpData;
	Gdiplus::PixelFormat pixelFormat = pImage->GetPixelFormat();
	Status status = pImage->LockBits( &rc, ImageLockModeRead, pixelFormat, &bmpData );

	mat.NewSize(bmpData.Height,bmpData.Width);
	
	BYTE* pDst=mat.GetPtr();
	BYTE* pSrc;
	UINT i,j;

	UINT PaletteSize;
	ColorPalette* palette;
	Color entry;

	int nByteCount = GetPixelByte(pixelFormat);
	if ( nByteCount==1 ) {
		PaletteSize=pImage->GetPaletteSize();
		palette= (ColorPalette*)new BYTE[PaletteSize];
		pImage->GetPalette(palette,PaletteSize);
	}

	for ( i=0; i<bmpData.Height; i++) {
		pSrc=(BYTE*)bmpData.Scan0 + i*bmpData.Stride;
		switch (nByteCount) {
		case 1:
			for ( j=0; j<bmpData.Width; j++) {
				entry=palette->Entries[*pSrc++];
				*pDst++=RGB2GrayScale(entry.GetR(), entry.GetG(), entry.GetB());
			}
			break;
		case 3:
		case 4:
			for ( j=0; j<bmpData.Width; j++) {
				//  ( R*77+G*151+B*28 ) /256
				*pDst++=RGB2GrayScale(pSrc[2], pSrc[1], pSrc[0]);
				pSrc+=nByteCount;
			}
			break;

		}
	}

	if ( nByteCount==1 ) {
		delete[] palette;
	}

	pImage->UnlockBits(&bmpData);
	return true;
}

bool Img2RGBMat(Gdiplus::Bitmap *pImage, TUIntMat& mat)
{
	if ( !pImage) 
		return false;
	Rect rc(0, 0, pImage->GetWidth(), pImage->GetHeight());
	Gdiplus::BitmapData bmpData;
	Gdiplus::PixelFormat pixelFormat = pImage->GetPixelFormat();
	Status status = pImage->LockBits( &rc, ImageLockModeRead, pixelFormat, &bmpData );

	mat.NewSize(bmpData.Height,bmpData.Width);
	
	UINT* pDst=mat.GetPtr();
	BYTE* pSrc;
	CRGBPixel pixel;
	UINT i,j;

	UINT PaletteSize;
	ColorPalette* palette;
	Color entry;

	int nByteCount = GetPixelByte(pixelFormat);
	if ( nByteCount==1 ) {
		PaletteSize=pImage->GetPaletteSize();
		palette= (ColorPalette*)new BYTE[PaletteSize];
		pImage->GetPalette(palette,PaletteSize);
	}

	for ( i=0; i<bmpData.Height; i++) {
		pSrc=(BYTE*)bmpData.Scan0 + i*bmpData.Stride;
		switch (nByteCount) {
		case 1:
			for ( j=0; j<bmpData.Width; j++) {
				entry=palette->Entries[*pSrc++];
				pixel.R()=entry.GetR();
				pixel.G()=entry.GetG();
				pixel.B()=entry.GetB();
				*pDst++=(unsigned int)pixel;
			}
			break;
		case 3:
		case 4:
			for ( j=0; j<bmpData.Width; j++) {
				pixel.R()=pSrc[2];
				pixel.G()=pSrc[1];
				pixel.B()=pSrc[0];
				*pDst++=(unsigned int)pixel;
				pSrc+=nByteCount;
			}
			break;

		}
	}

	if ( nByteCount==1 ) {
		delete[] palette;
	}
	pImage->UnlockBits(&bmpData);	
	return true;
}

bool CGdiImage::Img2Mat(TByteMat& mat)
{
	return ::Img2Mat(m_pImage,mat);
}

bool CGdiImage::Mat2Img(const TByteMat& mat)
{
	Free();
	int nRows=mat.GetRows(), nCols=mat.GetCols();
	BYTE *pBuf, *pBmpHeader;

	Gdiplus::Bitmap* pImg=GetImgFromBuf(mat.GetPtr(), nRows, nCols, PixelFormat8bppIndexed , pBuf, pBmpHeader);
	
	m_pImage= new  Gdiplus::Bitmap(nCols,nRows, PixelFormat24bppRGB);
	Gdiplus::Graphics gr(m_pImage);

	gr.DrawImage(pImg,0,0, nCols, nRows);

	delete pImg;
	delete []pBmpHeader;
	delete []pBuf;
	return true;
}

bool CGdiImage::Img2RGBMat(TUIntMat& mat)
{
	return ::Img2RGBMat(m_pImage,mat);
}

bool CGdiImage::RGBMat2Img(const TUIntMat& mat)
{
	Free();
	int nRows=mat.GetRows(), nCols=mat.GetCols();
	BYTE *pBuf, *pBmpHeader;

	Gdiplus::Bitmap* pImg=GetImgFromBuf((BYTE*)mat.GetPtr(), nRows, nCols, PixelFormat24bppRGB , pBuf, pBmpHeader);

	m_pImage= new  Gdiplus::Bitmap(nCols,nRows,PixelFormat24bppRGB);
	Gdiplus::Graphics gr(m_pImage);

	gr.DrawImage(pImg,0,0, nCols, nRows);

	delete pImg;
	delete []pBmpHeader;
	delete []pBuf;
	return true;
}

bool CGdiImage::Save(const char* pszName, int imageFormat)
{
	WCHAR wzFileName[_MAX_PATH];
	const WCHAR* EncoderList[]={L"image/bmp", L"image/jpeg"};
	
	swprintf_s( wzFileName, _MAX_PATH, L"%S", pszName );
	
	CLSID bmpEncoder;
	
	int result = GetEncoderClsid( EncoderList[imageFormat], &bmpEncoder );
	if( result < 0 )	{
		cout << "Encoder error" << endl;
		return false;
	}
	result = m_pImage->Save( wzFileName, &bmpEncoder, NULL );
	if( result != Ok )
		cout << "Failed to write file " << pszName << ". Please check the disk" << endl;

	//cout << "Saved!" << endl;

	return true;
}

bool Mat2Bmp(const TByteMat& mat,const char* pszName, int imageFormat)
{
	int nRows=mat.GetRows(), nCols=mat.GetCols();
	BYTE *pBuf, *pBmpHeader;
	Gdiplus::Bitmap* pImg=GetImgFromBuf(mat.GetPtr(), nRows, nCols, PixelFormat8bppIndexed , pBuf, pBmpHeader);
	Save(pImg, pszName,imageFormat);
	
	delete[] pBuf;
	delete[] pBmpHeader;
	delete pImg;

	return true;
}

bool Mat2Bmp(const TUIntMat& mat,const char* pszName, int imageFormat)
{
	int nRows=mat.GetRows(), nCols=mat.GetCols();
	BYTE *pBuf, *pBmpHeader;
	Gdiplus::Bitmap* pImg=GetImgFromBuf((BYTE*)mat.GetPtr(), nRows, nCols, PixelFormat24bppRGB , pBuf, pBmpHeader);
	Save(pImg,pszName,imageFormat);
	
	delete[] pBuf;
	delete[] pBmpHeader;
	delete pImg;

	return true;
}

Gdiplus::Bitmap *GetImgFromBuf(BYTE* pBufSrc, int nRows, int nCols, Gdiplus::PixelFormat pixelFormat, BYTE*& pBuf, BYTE*& pBmpHeader)
{
	UINT nStride, i,j;
	BYTE *pDst;
	BITMAPINFO * pBmpInfo;
	int nHeadLen, nBit, nClrUsed;
	
	pBuf=pBmpHeader=0;
	if (pixelFormat == PixelFormat24bppRGB) {
		nStride=((nCols*3+3)/4)*4;
		nHeadLen = sizeof(BITMAPINFO);
		nBit=24;
		nClrUsed = 0;
	}
	else if (pixelFormat == PixelFormat8bppIndexed ) {
		nStride=((nCols+3)/4)*4;
		nHeadLen = sizeof(BITMAPINFO) + 255*sizeof(RGBQUAD);
		nBit=8;
		nClrUsed = 256;
	}
	else 
		return 0;
	
	pBuf = new BYTE[nRows*nStride];

	if (pixelFormat == PixelFormat24bppRGB) {	
		unsigned int* pSrc;
		CRGBPixel pixel;
		for ( i=0; i<(unsigned)nRows; i++ ) {
			pDst=pBuf+nStride*i;
			pSrc=(unsigned int*)pBufSrc+i*nCols;
			for ( j=0; j<(unsigned)nCols; j++ ) {
				pixel=pSrc[j];
				*pDst++=pixel.B();
				*pDst++=pixel.G();
				*pDst++=pixel.R();
			}
		}
	}
	else if (pixelFormat == PixelFormat8bppIndexed ) {
		BYTE* pSrc;
		for ( i=0; i<(unsigned)nRows; i++ ) {
			pSrc=pBufSrc+i*nCols;
			pDst=pBuf+i*nStride;
			TArrayCpy(pDst,pSrc,nCols);
		}
	}

	pBmpHeader=new BYTE[ nHeadLen ];
	pBmpInfo = (BITMAPINFO *)pBmpHeader;
	pBmpInfo->bmiHeader.biBitCount = nBit;
	pBmpInfo->bmiHeader.biCompression = 0;
	pBmpInfo->bmiHeader.biHeight = -1 * nRows;
	pBmpInfo->bmiHeader.biWidth = nCols;
	pBmpInfo->bmiHeader.biSize = nRows*nStride;
	pBmpInfo->bmiHeader.biPlanes = 1;
	pBmpInfo->bmiHeader.biClrUsed = nClrUsed;
	
	if (pixelFormat == PixelFormat8bppIndexed ) {
		for( i=0; i<256; ++i)	{
			pBmpInfo->bmiColors[i].rgbBlue = i;
			pBmpInfo->bmiColors[i].rgbGreen = i;
			pBmpInfo->bmiColors[i].rgbRed = i;
			pBmpInfo->bmiColors[i].rgbReserved = 0;
		}
	}

	return Gdiplus::Bitmap::FromBITMAPINFO(pBmpInfo, pBuf);
}

int GetPixelByte(PixelFormat pixelFormat )
{
	int nBitCount;
	switch (pixelFormat)
	{
	case PixelFormat1bppIndexed:
		nBitCount=1;
		break;
	case  PixelFormat4bppIndexed:
		nBitCount=4;
		break;
	case  PixelFormat8bppIndexed:
		nBitCount=8;
		break;
	case  PixelFormat16bppGrayScale:
	case  PixelFormat16bppRGB555:
	case  PixelFormat16bppRGB565:
	case  PixelFormat16bppARGB1555:
		nBitCount=16;
		break;
	case  PixelFormat24bppRGB:
		nBitCount=24;
		break;
	case  PixelFormat32bppRGB:
	case  PixelFormat32bppARGB:
	case  PixelFormat32bppPARGB:
		nBitCount=32;
		break;
	case  PixelFormat48bppRGB:
		nBitCount=48;
		break;
	case  PixelFormat64bppARGB:
	case  PixelFormat64bppPARGB:
		nBitCount=64;
		break;
	default:
		nBitCount=-1;
	}
  
	return nBitCount>>3;
}


bool Save(Gdiplus::Bitmap *pImage, const char* pszName, int imageFormat)
{
	WCHAR wzFileName[_MAX_PATH];
	const WCHAR* EncoderList[]={L"image/bmp", L"image/jpeg"};
	
	swprintf_s( wzFileName, _MAX_PATH, L"%S", pszName );
	
	CLSID bmpEncoder;
	
	int result = GetEncoderClsid( EncoderList[imageFormat], &bmpEncoder );
	if( result < 0 )	{
		cout << "Encoder error" << endl;
		return false;
	}
	result = pImage->Save( wzFileName, &bmpEncoder, NULL );
	if( result != Ok )
		cout << "Failed to write file " << pszName << ". Please check the disk" << endl;

	//cout << "Saved!" << endl;

	return true;
}
